package mylittleinvestigations;

import mylittleinvestigations.CaseInformation.CaseXmlWriter;
import mylittleinvestigations.CaseInformation.Case;
import mylittleinvestigations.CaseInformation.CaseXmlReader;
import mylittleinvestigations.CaseInformation.CaseParseException;
import java.io.IOException;
import java.util.ArrayList;
import mylittleinvestigations.Collisions.HitBox;
import org.newdawn.slick.*;
import org.newdawn.slick.geom.*;

public class ForegroundElement implements InteractiveElement, ZOrderableObject
{
    private Sprite sprite;
    private String textureId;
    private HitBox hitBox;
    protected Polygon clickPolygon;

    protected Encounter encounter;
    private String zoomedViewId;

    protected Vector2 position;
    protected int anchorPosition;
    protected Line zOrderLine;
    
    private ArrayList<ForegroundElementAnimation> foregroundElementAnimationList;

    public ForegroundElement()
    {
    }

    public ForegroundElement(String textureId, Vector2 position, int anchorPosition, Line zOrderLine, ArrayList<ForegroundElementAnimation> foregroundElementAnimationList, HitBox hitBox, Polygon clickPolygon, Conversation conversation, String zoomedViewId)
    {
        this.textureId = textureId;
        this.position = position;
        this.anchorPosition = anchorPosition;
        this.zOrderLine = zOrderLine;
        this.hitBox = hitBox;
        this.clickPolygon = clickPolygon;

        if (conversation != null)
        {
            this.encounter = new Encounter();
            this.encounter.setOneShotConversation(conversation);
        }
        
        this.zoomedViewId = zoomedViewId;
        
        this.foregroundElementAnimationList = foregroundElementAnimationList;
    }

    private Sprite getSprite()
    {
        if (this.sprite == null && this.textureId != null)
        {
            this.sprite = Case.getInstance().getSpriteManager().GetSpriteFromId(this.textureId);
        }

        return this.sprite;
    }
    
    private String name;
    
    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }
    
    private Vector2 interactionLocation;

    public Vector2 getInteractionLocation() { return this.interactionLocation; }
    public void setInteractionLocation(Vector2 interactionLocation) { this.interactionLocation = interactionLocation; }
    
    private boolean interactFromAnywhere;
    
    public boolean getInteractFromAnywhere() { return this.interactFromAnywhere; }
    public void setInteractFromAnywhere(boolean interactFromAnywhere) { this.interactFromAnywhere = interactFromAnywhere; }
    
    private boolean isClicked;
    
    public boolean getIsClicked() { return this.isClicked; }
    public void setIsClicked(boolean isClicked) { this.isClicked = isClicked; }
    
    private Condition condition;
    
    public Condition getCondition() { return this.condition; }
    public void setCondition(Condition condition) { this.condition = condition; }

    public boolean getIsInteractive()
    {
        return this.encounter != null;
    }

    public boolean getIsInteractionFinished()
    {
        return
            (this.encounter == null || this.encounter.getIsFinished());
    }

    public boolean getIsInteractionFullScreen()
    {
        return
            (this.encounter == null || this.encounter.getIsFinished());
    }
    
    public void Begin()
    {
        for (int i = 0; i < this.foregroundElementAnimationList.size(); i++)
        {
            this.foregroundElementAnimationList.get(i).Begin();
        }
    }

    public void Update(GameContainer gc, int delta)
    {
        Polygon adjustedClickPolygon = this.clickPolygon;
        this.Update(gc, delta, adjustedClickPolygon);
    }
    
    public void Update(GameContainer gc, int delta, Vector2 offsetVector)
    {
        Polygon adjustedClickPolygon = (Polygon)this.clickPolygon.transform(Transform.createTranslateTransform((int)(-offsetVector.getX()), (int)(-offsetVector.getY())));
        this.Update(gc, delta, adjustedClickPolygon);
    }
    
    public void Update(GameContainer gc, int delta, Polygon adjustedClickPolygon)
    {
        for (int i = 0; i < this.foregroundElementAnimationList.size(); i++)
        {
            this.foregroundElementAnimationList.get(i).Update(gc, delta);
        }
        
        if (MouseHelper.MouseOverPolygon(adjustedClickPolygon) || MouseHelper.MouseDownOnPolygon(adjustedClickPolygon))
        {
            MouseHelper.SetToHandCursor();
        }

        this.setIsClicked(MouseHelper.ClickedOnPolygon(adjustedClickPolygon));
    }

    public void Draw(GameContainer gc, Graphics g)
    {
        if (this.textureId != null)
        {
            this.getSprite().Draw(gc, g, this.position);
        }
    }

    public void Draw(GameContainer gc, Graphics g, Vector2 offsetVector)
    {
        Vector2 screenPosition = this.position.subtract(offsetVector);
        
        if (this.textureId != null)
        {
            this.getSprite().Draw(gc, g, screenPosition);
        }
        
        for (int i = 0; i < this.foregroundElementAnimationList.size(); i++)
        {
            this.foregroundElementAnimationList.get(i).Draw(gc, g, screenPosition);
        }
    }

    public void BeginInteraction()
    {
        if (this.encounter != null)
        {
            this.encounter.Begin();
        }
    }

    public void UpdateInteraction(GameContainer gc, int delta) throws CaseParseException
    {
        if (this.encounter != null && !this.encounter.getIsFinished())
        {
            this.encounter.Update(gc, delta);
        }
    }

    public void DrawInteraction(GameContainer gc, Graphics g) throws SlickException
    {
        if (this.encounter != null && !this.encounter.getIsFinished())
        {
            this.encounter.Draw(gc, g);
        }
    }

    public Vector2 GetCenterPoint()
    {
        return new Vector2(
                this.clickPolygon.getMinX() + this.clickPolygon.getWidth() / 2,
                this.clickPolygon.getMinY() + this.clickPolygon.getHeight() / 2);
    }

    public Rectangle GetBoundsForInteraction()
    {
        return new Rectangle(
                (float)this.clickPolygon.getMinX(),
                (float)this.clickPolygon.getMinY(),
                (float)this.clickPolygon.getWidth(),
                (float)this.clickPolygon.getHeight());
    }

    public void BeginInteraction(Location location)
    {
        location.BeginForegroundElementInteraction(this);
    }

    public int GetZOrder()
    {
        if (this.anchorPosition >= 0)
        {
            return (int)this.position.getY() + this.anchorPosition;
        }
        else
        {
            return -1;
        }
    }

    public boolean IsVisible()
    {
        return this.textureId != null;
    }
    
    public Line GetZOrderLine()
    {
        return this.zOrderLine;
    }
    
    public Vector2 GetZOrderPoint()
    {
        if (this.anchorPosition >= 0)
        {
            return new Vector2(this.position.getX() + this.getSprite().getWidth() / 2, this.position.getY() + this.anchorPosition);
        }
        else
        {
            return new Vector2(0, -1);
        }
    }

    public void SaveToXml(CaseXmlWriter writer) throws IOException
    {
        writer.StartElement("ForegroundElement");
        
        this.SaveToXmlCore(writer);
        
        writer.EndElement();
    }
    
    protected void SaveToXmlCore(CaseXmlWriter writer) throws IOException
    {
        if (this.textureId != null)
        {
            writer.WriteSpriteId("SpriteId", this.textureId);
        }
        
        if (this.name != null)
        {
            writer.WriteTextElement("Name", this.name);
        }
        
        writer.StartElement("Position");
        this.position.SaveToXml(writer);
        writer.EndElement();
        
        writer.WriteIntElement("AnchorPosition", this.anchorPosition);
        
        if (this.interactionLocation != null)
        {
            writer.StartElement("InteractionLocation");
            this.interactionLocation.SaveToXml(writer);
            writer.EndElement();
        }
            
        writer.WriteBooleanElement("InteractFromAnywhere", this.interactFromAnywhere);
        
        if (this.hitBox != null)
        {
            writer.StartElement("HitBox");
            this.hitBox.SaveToXml(writer);
            writer.EndElement();
        }
        
        writer.StartElement("ClickPolygon");
        writer.WritePolygonElement(this.clickPolygon);
        writer.EndElement();

        if (this.encounter != null)
        {
            writer.StartElement("Encounter");
            this.encounter.SaveToXml(writer);
            writer.EndElement();
        }
        
        if (this.zoomedViewId != null)
        {
            writer.WriteTextElement("ZoomedViewId", this.zoomedViewId);
        }
        
        writer.StartElement("Animations");
        
        for (int i = 0; i < this.foregroundElementAnimationList.size(); i++)
        {
            writer.StartElement("Entry");
            this.foregroundElementAnimationList.get(i).SaveToXml(writer);
            writer.EndElement();
        }
        
        writer.EndElement();
        
        if (this.zOrderLine != null)
        {
            writer.StartElement("ZOrderLine");
            this.zOrderLine.SaveToXml(writer);
            writer.EndElement();
        }
            
        if (this.condition != null)
        {
            writer.StartElement("Condition");
            this.condition.SaveToXml(writer);
            writer.EndElement();
        }
    }

    public ForegroundElement(CaseXmlReader reader)
    {
        this.foregroundElementAnimationList = new ArrayList<ForegroundElementAnimation>();
        
        reader.StartElement("ForegroundElement");
        
        if (reader.ElementExists("SpriteId"))
        {
            this.textureId = reader.ReadTextElement("SpriteId");
        }
        
        if (reader.ElementExists("Name"))
        {
            this.name = reader.ReadTextElement("Name");
        }
        
        reader.StartElement("Position");
        this.position = new Vector2(reader);
        reader.EndElement();
        
        this.anchorPosition = reader.ReadIntElement("AnchorPosition");
        
        if (reader.ElementExists("HitBox"))
        {
            reader.StartElement("HitBox");
            this.hitBox = new HitBox(reader);
            reader.EndElement();
        }
        
        reader.StartElement("ClickPolygon");
        this.clickPolygon = reader.ReadPolygonElement();
        reader.EndElement();

        if (reader.ElementExists("Encounter"))
        {
            reader.StartElement("Encounter");
            this.encounter = new Encounter(reader);
            reader.EndElement();
        }
        
        reader.StartElement("Animations");
        reader.StartList("Entry");
        
        while (reader.MoveToNextListItem())
        {
            this.foregroundElementAnimationList.add(new ForegroundElementAnimation(reader));
        }
        
        reader.EndElement();
        
        if (reader.ElementExists("ZOrderLine"))
        {
            reader.StartElement("ZOrderLine");
            this.zOrderLine = new Line(reader);
            reader.EndElement();
        }
        
        reader.EndElement();
    }
    
    public void markChildSprites(String markParent) throws CaseParseException
    {
        for (ForegroundElementAnimation fea : foregroundElementAnimationList)
        {
            fea.markChildSprites(markParent);
        }
        
        if (this.textureId != null)
        {
            this.getSprite().markChildSprites(markParent);
        }
        
        if (this.encounter != null)
        {
            this.encounter.markChildSprites(markParent);
        }
    }
}